
#include <qlib/geo/ellipse.h>
#include <qlib/geo/rectangle.h>
#include <algorithm>

namespace qlib {
Ellipse::Ellipse() {
  tolerance = 0;
  rotation = 0;
  layer = 0;
  datatype = 0;
  tolerance = 0.1;
  point_num = 40;
}

Ellipse::Ellipse(Vec2d _center, Vec2d _radius, float _rotation, double _tolerance, uint32_t _layer, uint32_t _datatype) {
  center = _center;
  radius = _radius;
  rotation = _rotation;
  tolerance = _tolerance;
  layer = _layer;
  datatype = _datatype;
  point_num = 40;
}

Ellipse::Ellipse(const Ellipse& oth) {
  operator=(oth);
}
Ellipse& Ellipse::operator=(const Ellipse& other) {
  center = other.center;
  radius = other.radius;
  point_num = other.point_num;
  tolerance = other.tolerance;
  rotation = other.rotation;
  layer = other.layer;
  datatype = other.datatype;
  repetition = other.repetition;

  properties.resize(other.properties.size());
  for (size_t i = 0; i < properties.size(); i++) {
    if (other.properties[i]) {
      properties[i] = Property::create();
      *properties[i] = *other.properties[i];
    }
  }

  polygon = to_polygon();

  return *this;
}

Ellipse::s_ptr Ellipse::copy() const {
  auto res = Ellipse::create();
  *res = *this;

  return res;
};

void Ellipse::init(Vec2d _center, Vec2d _radius, float _rotation, double _tolerance, uint32_t _layer, uint32_t _datatype) {
  center = _center;
  radius = _radius;
  rotation = _rotation;
  tolerance = _tolerance;
  layer = _layer;
  datatype = _datatype;
}

void Ellipse::scale(double sx, double sy) {
  radius *= Vec2d(sx, sy);
}

Ellipse getEllipse(qlib::Polygon::s_ptr polygon, double tolerance = 1e-6) {
  Ellipse res_ellipse;

  double dis = 0;
  double max_dis = -DBL_MAX;
  double min_dis = DBL_MAX;

  auto box = polygon->bounding_box();
  qlib::Vec2d center = (box[0] + box[1]) / 2.0;
  qlib::Vec2d max_pos;

  for (size_t i = 0; i < polygon->points.size(); i++) {
    dis = (polygon->points[i] - center).length();

    if (dis > max_dis) {
      max_dis = dis;
      max_pos = polygon->points[i];
    }

    if (dis < min_dis) {
      min_dis = dis;
    }
  }

  double rotation = (max_pos - center).angle();

  res_ellipse.center = center;
  res_ellipse.radius = qlib::Vec2d(min_dis, max_dis);
  res_ellipse.rotation = rotation;

  return res_ellipse;
}

// void Ellipse::mirror(Vec2d p0, Vec2d p1) {
//   Vec2d v = p1 - p0;
//   double tmp = v.length_sq();
//   if (tmp == 0)
//     return;

//   Vec2d r = v * (2 / tmp);
//   Vec2d p2 = p0 * 2;

//   Vec2d* p = &center;
//   *p = v * (*p - p0).inner(r) - *p + p2;
// }

void Ellipse::mirror(Vec2d p0, Vec2d p1) {
  // 1. 镜像中心点
  const Vec2d AB = p1 - p0;
  const Vec2d AP = center - p0;
  const double AB_sq = AB.dot(AB);

  // 避免除零
  if (AB_sq < 1e-20)
    return;

  const double t = AP.dot(AB) / AB_sq;
  center = 2.0 * (p0 + AB * t) - center;

  // 2. 计算镜像轴角度（快速atan2）
  const double alpha = std::atan2(AB.y, AB.x);

  // 3. 镜像旋转角度（使用预计算的正余弦）
  const double new_rot = 2.0 * alpha - rotation;

  // 角度标准化到[-π, π]
  rotation = std::remainder(new_rot, 2.0 * M_PI);
}

void Ellipse::translate(double dx, double dy) {
  center += Vec2d(dx, dy);
}
void Ellipse::transform(double magnification, bool x_reflection, Vec2d origin) {
  if (x_reflection) {
    center.y *= -1;
  }
  radius *= magnification;
  center *= magnification;
  center += origin;
}

void Ellipse::transform(double magnification, bool x_reflection, double rotation, Vec2d origin) {
  Polygon::s_ptr poly = to_polygon();
  poly->transform(magnification, x_reflection, rotation, origin);

  Vec2d pos_add;
  for (size_t i = 0; i < poly->points.size(); i++) {
    pos_add += poly->points[i];
  }

  pos_add /= poly->points.size();

  center = pos_add;
  center *= magnification;
}

Polygon::s_ptr Ellipse::to_polygon(bool is_tri) {
  Polygon::s_ptr result = Polygon::create();

  result->layer = layer;
  result->datatype = datatype;
  result->properties = properties;
  result->repetition = repetition;

  double radius_x = radius.x, radius_y = radius.y, inner_radius_x = 0, inner_radius_y = 0;
  double initial_angle = 0, final_angle = 2 * M_PI;

  const double full_angle = (final_angle == initial_angle) ? 2 * M_PI : fabs(final_angle - initial_angle);
  if (inner_radius_x > 0 && inner_radius_y > 0) {
    uint64_t num_points1 = 1 + arc_num_points(full_angle, radius_x > radius_y ? radius_x : radius_y, tolerance);
    if (num_points1 < GDSTK_MIN_POINTS)
      num_points1 = GDSTK_MIN_POINTS;
    uint64_t num_points2 = 1 + arc_num_points(full_angle, inner_radius_x > inner_radius_y ? inner_radius_x : inner_radius_y, tolerance);
    if (num_points2 < GDSTK_MIN_POINTS)
      num_points2 = GDSTK_MIN_POINTS;

    result->points.resize(num_points1 + num_points2);
    Vec2d* v = result->points.data();
    if (full_angle == 2 * M_PI) {
      // Ring
      for (uint64_t i = 0; i < num_points1; i++) {
        const double angle = i * 2 * M_PI / (num_points1 - 1);
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
      for (uint64_t i = num_points2; i > 0; i--) {
        const double angle = (i - 1) * 2 * M_PI / (num_points2 - 1);
        *v++ = center + Vec2{inner_radius_x * cos(angle), inner_radius_y * sin(angle)};
      }
    } else {
      // Ring slice
      double initial_ell_angle = elliptical_angle_transform(initial_angle, radius_x, radius_y);
      double final_ell_angle = elliptical_angle_transform(final_angle, radius_x, radius_y);
      for (uint64_t i = 0; i < num_points1; i++) {
        const double angle = LERP(initial_ell_angle, final_ell_angle, (double)i / (double)(num_points1 - 1));
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
      initial_ell_angle = elliptical_angle_transform(initial_angle, inner_radius_x, inner_radius_y);
      final_ell_angle = elliptical_angle_transform(final_angle, inner_radius_x, inner_radius_y);
      for (uint64_t i = num_points2; i > 0; i--) {
        const double angle = LERP(initial_ell_angle, final_ell_angle, (double)(i - 1) / (double)(num_points2 - 1));
        *v++ = center + Vec2{inner_radius_x * cos(angle), inner_radius_y * sin(angle)};
      }
    }
  } else {
    uint64_t num_points = 1 + arc_num_points(full_angle, radius_x > radius_y ? radius_x : radius_y, tolerance);
    if (num_points < GDSTK_MIN_POINTS)
      num_points = GDSTK_MIN_POINTS;
    if (full_angle == 2 * M_PI) {
      // Full ellipse
      num_points = point_num;
      result->points.resize(num_points);
      Vec2d* v = result->points.data();
      for (uint64_t i = 0; i < num_points; i++) {
        const double angle = i * 2 * M_PI / num_points;
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
    } else {
      // Slice
      const double initial_ell_angle = elliptical_angle_transform(initial_angle, radius_x, radius_y);
      const double final_ell_angle = elliptical_angle_transform(final_angle, radius_x, radius_y);
      num_points = point_num;
      result->points.resize(num_points + 1);
      Vec2d* v = result->points.data();
      *v++ = center;
      for (uint64_t i = 0; i < num_points; i++) {
        const double angle = LERP(initial_ell_angle, final_ell_angle, (double)i / (num_points - 1.0));
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
    }
  }

  result->rotate(rotation, center);

  if (is_tri) {
    polygon = result;
    polygon->genTriIndices();
  }
  return result;
}

Vec2dArray Ellipse::bounding_box() {
  Vec2dArray res(2);
  Vec2d& min = res[0];
  Vec2d& max = res[1];
  if (rotation == 0) {
    min = center - radius;
    max = center + radius;
  } else {
    auto poly = to_polygon();
    res = poly->bounding_box();
  }

  if (repetition.type != RepetitionType::None) {
    // 获取重复下的极值
    Vec2dArray offsets = {};
    repetition.get_extrema(offsets);

    Vec2d* off = offsets.data();
    Vec2d min0 = min;
    Vec2d max0 = max;

    // 将极值应用到每个包围盒上
    for (uint64_t i = offsets.size(); i > 0; i--, off++) {
      if (min0.x + off->x < min.x)
        min.x = min0.x + off->x;
      if (max0.x + off->x > max.x)
        max.x = max0.x + off->x;
      if (min0.y + off->y < min.y)
        min.y = min0.y + off->y;
      if (max0.y + off->y > max.y)
        max.y = max0.y + off->y;
    }
  }

  return res;
}

Vec2dArray Ellipse::obb() {
  Vec2dArray obb_box(2);
  obb_box[0] = center - radius;
  obb_box[1] = center + radius;
  auto rect = Rectangle::create(obb_box[0], obb_box[1]);
  rect->rotation = rotation;
  return rect->to_polygon()->points;
}

Vec2dArray Ellipse::render_aabb() {
  return bounding_box();
}

Vec2dArray Ellipse::render_poly() {
  auto p = to_polygon();
  return p->points;
}

Vec2dArray Ellipse::render_absorp() {
  auto p = to_polygon();
  return p->points;
}

void Ellipse::rotate(double angle, Vec2d _center) {
  rotation += angle;

  qlib::Vec2dArray points(1);
  Vec2d vt = center - _center;

  double ca = cos(angle);
  double sa = sin(angle);

  points[0].x = vt.x * ca - vt.y * sa + _center.x;
  points[0].y = vt.x * sa + vt.y * ca + _center.y;

  center = points[0];
}

uint32_t Ellipse::getLayer() {
  return layer;
}
void Ellipse::setLayer(uint32_t _layer) {
  layer = _layer;
}

Vec2d Ellipse::getCenterPoint() {
  return center;
}
void Ellipse::setCenterPoint(Vec2d _center) {
  center = _center;
  // auto trans = center - _center;
  // translate(trans.x, trans.y);
}

double Ellipse::getRotation() {
  return rotation;
}

void Ellipse::setRotation(double _rotation) {
  rotation = _rotation;
}

int Ellipse::getPointNum() {
  return point_num;
}
void Ellipse::setPointNum(int num) {
  point_num = num;
}

qlib::Vec2d Ellipse::getRadius() {
  return radius;
}

void Ellipse::setRadius(qlib::Vec2d _radius) {
  radius = _radius;
}

double Ellipse::area() const {
  return M_PI * radius.x * radius.y;
}

EllipseParts Ellipse::get_parts() {
  auto poly = to_polygon();
  auto v_points = connect_end(poly->points);
  auto v_edges = get_edges(v_points);
  auto v_edges_mid = get_edges_mid(v_edges);

  auto box = bounding_box();
  auto box_points = box_to_points(box);
  auto box_edges = get_edges(box_points);
  auto box_edges_mid = get_edges_mid(box_edges);

  auto res = std::make_tuple(center, v_points, v_edges, v_edges_mid, box_points, box_edges, box_edges_mid);
  return res;
}

Vec2dArray Ellipse::query_parts(GeoPartType type, uint32_t id) {
  auto parts = get_parts();
  if (type == GeoPartType::Center)
    return Vec2dArray{std::get<0>(parts)};
  if (type == GeoPartType::Point)
    return Vec2dArray{std::get<1>(parts)[id]};
  if (type == GeoPartType::Edge)
    return std::get<2>(parts)[id];
  if (type == GeoPartType::EdgeMid)
    return Vec2dArray{std::get<3>(parts)[id]};
  if (type == GeoPartType::BoxPoint)
    return Vec2dArray{std::get<4>(parts)[id]};
  if (type == GeoPartType::BoxEdge)
    return std::get<5>(parts)[id];
  if (type == GeoPartType::BoxEdgeMid)
    return Vec2dArray{std::get<6>(parts)[id]};
  return Vec2dArray{};
}

}  // namespace qlib